﻿using System;
using System.Globalization;
using System.Runtime.CompilerServices;

namespace LINQPadLogParser
{
  public sealed class DateTimeWrapper : IFormattable
  {
    private readonly int _id;
    private string _date;
    private DateTime _dateTime;
    private string _time;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTimeWrapper(int id)
    {
      _id = id;
    }

    private string DateString
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _date ?? (_date = LogParserDataContext.LineManager.GetValue(_id, W3CColumn.Date)); }
    }

    private string TimeString
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _time ?? (_time = LogParserDataContext.LineManager.GetValue(_id, W3CColumn.Time)); }
    }

    private DateTime DateTime
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        if (_dateTime == default(DateTime))
        {
          _dateTime = DateTime.Parse(DateString + " " + TimeString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
        }
        return _dateTime;
      }
    }

    public DateTime Date
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.Date; }
    }

    public int Day
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.Day; }
    }

    public DayOfWeek DayOfWeek
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.DayOfWeek; }
    }

    public int DayOfYear
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.DayOfYear; }
    }

    public int Hour
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.Hour; }
    }

    public DateTimeKind Kind
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTimeKind.Utc; }
    }

    public int Millisecond
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return 0; }
    }

    public int Minute
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.Minute; }
    }

    public int Month
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.Month; }
    }

    public int Second
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.Second; }
    }

    public long Ticks
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.Ticks; }
    }

    public TimeSpan TimeOfDay
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.TimeOfDay; }
    }

    public int Year
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return DateTime.Year; }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string ToString(string format, IFormatProvider provider)
    {
      return DateTime.ToString(format, provider);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private bool Equals(DateTimeWrapper other)
    {
      return string.Equals(DateString, other.DateString) && string.Equals(TimeString, other.TimeString);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj)
    {
      if (ReferenceEquals(null, obj)) return false;
      if (ReferenceEquals(this, obj)) return true;
      return obj is DateTimeWrapper && Equals((DateTimeWrapper) obj);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override int GetHashCode()
    {
      unchecked
      {
        return ((DateString != null ? DateString.GetHashCode() : 0)*397) ^ (TimeString != null ? TimeString.GetHashCode() : 0);
      }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime Add(TimeSpan value)
    {
      return DateTime.Add(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime AddDays(double value)
    {
      return DateTime.AddDays(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime AddHours(double value)
    {
      return DateTime.AddHours(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime AddMilliseconds(double value)
    {
      return DateTime.AddMilliseconds(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime AddMinutes(double value)
    {
      return DateTime.AddMinutes(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime AddMonths(int months)
    {
      return DateTime.AddMonths(months);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime AddSeconds(double value)
    {
      return DateTime.AddSeconds(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime AddTicks(long value)
    {
      return DateTime.AddTicks(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime AddYears(int value)
    {
      return DateTime.AddYears(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int CompareTo(object value)
    {
      return DateTime.CompareTo(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int CompareTo(DateTime value)
    {
      return DateTime.CompareTo(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool Equals(DateTime value)
    {
      return DateTime.Equals(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool IsDaylightSavingTime()
    {
      return DateTime.IsDaylightSavingTime();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public long ToBinary()
    {
      return DateTime.ToBinary();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public TimeSpan Subtract(DateTime value)
    {
      return DateTime.Subtract(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime Subtract(TimeSpan value)
    {
      return DateTime.Subtract(value);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public double ToOADate()
    {
      return DateTime.ToOADate();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public long ToFileTime()
    {
      return DateTime.ToFileTime();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public long ToFileTimeUtc()
    {
      return DateTime.ToFileTimeUtc();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime ToLocalTime()
    {
      return DateTime.ToLocalTime();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string ToLongDateString()
    {
      return DateString;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string ToLongTimeString()
    {
      return TimeString;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string ToShortDateString()
    {
      return DateString;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string ToShortTimeString()
    {
      return TimeString;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string ToString(string format)
    {
      return DateTime.ToString(format);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string ToString(IFormatProvider provider)
    {
      return DateTime.ToString(provider);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime ToUniversalTime()
    {
      return DateTime.ToUniversalTime();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string[] GetDateTimeFormats()
    {
      return DateTime.GetDateTimeFormats();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string[] GetDateTimeFormats(IFormatProvider provider)
    {
      return DateTime.GetDateTimeFormats(provider);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string[] GetDateTimeFormats(char format)
    {
      return DateTime.GetDateTimeFormats(format);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string[] GetDateTimeFormats(char format, IFormatProvider provider)
    {
      return DateTime.GetDateTimeFormats(format, provider);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public TypeCode GetTypeCode()
    {
      return DateTime.GetTypeCode();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override string ToString()
    {
      return DateString + " " + TimeString;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator DateTime(DateTimeWrapper wrapper)
    {
      return wrapper.DateTime;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static DateTime operator +(DateTimeWrapper d, TimeSpan t)
    {
      return d.DateTime + t;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static DateTime operator -(DateTimeWrapper d, TimeSpan t)
    {
      return d.DateTime - t;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static TimeSpan operator -(DateTimeWrapper d1, DateTime d2)
    {
      return d1.DateTime - d2;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(DateTimeWrapper d1, DateTime d2)
    {
      return d1 != null && d1.DateTime == d2;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(DateTimeWrapper d1, DateTime d2)
    {
      return d1 != null && d1.DateTime != d2;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator <(DateTimeWrapper t1, DateTime t2)
    {
      return t1.DateTime < t2;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator <=(DateTimeWrapper t1, DateTime t2)
    {
      return t1.DateTime <= t2;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator >(DateTimeWrapper t1, DateTime t2)
    {
      return t1.DateTime > t2;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator >=(DateTimeWrapper t1, DateTime t2)
    {
      return t1.DateTime >= t2;
    }


    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static TimeSpan operator -(DateTimeWrapper d1, DateTimeWrapper d2)
    {
      return d1.DateTime - d2.DateTime;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(DateTimeWrapper d1, DateTimeWrapper d2)
    {
      return Compare(d1, d2) == 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(DateTimeWrapper d1, DateTimeWrapper d2)
    {
      return Compare(d1, d2) != 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator <(DateTimeWrapper t1, DateTimeWrapper t2)
    {
      return Compare(t1, t2) < 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator <=(DateTimeWrapper t1, DateTimeWrapper t2)
    {
      return Compare(t1, t2) <= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator >(DateTimeWrapper t1, DateTimeWrapper t2)
    {
      return Compare(t1, t2) > 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator >=(DateTimeWrapper t1, DateTimeWrapper t2)
    {
      return Compare(t1, t2) >= 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static int Compare(DateTimeWrapper t1, DateTimeWrapper t2)
    {
      if (t1._dateTime != default(DateTime) && t2._dateTime != default(DateTime))
        return DateTime.Compare(t1._dateTime, t2._dateTime);
      int value = string.CompareOrdinal(t1.DateString, t2.DateString);
      if (value != 0)
        return value;
      return string.CompareOrdinal(t1.TimeString, t2.TimeString);
    }
  }
}